package io.github.luons.mediator.generator.model;

import io.github.luons.docs.node.FieldNode;
import io.github.luons.docs.node.ResponseNode;
import io.github.luons.mediator.generator.entity.ModelProBean;
import io.github.luons.mediator.generator.entity.YmlConstant;
import io.github.luons.mediator.generator.utils.YmlUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;

import java.text.MessageFormat;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * ResponseYml
 *
 * @author : luons
 */
public class ResponseYml {

    public static LinkedHashMap<Integer, Object> genResponse(String filePath, String operatorId, ResponseNode node) {
        LinkedHashMap<Integer, Object> response = new LinkedHashMap<>();
        LinkedHashMap<String, Object> ok = new LinkedHashMap<>();
        ok.put("description", "返回结果");
        ok.put("schema", genSchemaMap(filePath, operatorId, node.getChildNodes()));

        response.put(200, ok);
        return response;
    }

    public static Map<String, Object> genSchemaMap(String filePath, String operatorId, List<FieldNode> fieldNodes) {
        LinkedHashMap<String, Object> schema = new LinkedHashMap<>();
        if (CollectionUtils.isEmpty(fieldNodes)) {
            return schema;
        }

        LinkedHashMap<String, Object> pro = new LinkedHashMap<>();
        for (FieldNode fieldNode : fieldNodes) {
            if (Objects.isNull(fieldNode)) {
                continue;
            }
            if ("error".equalsIgnoreCase(fieldNode.getName())) {
                LinkedHashMap<String, Object> err = new LinkedHashMap<>();
                err.put("description", "错误信息");
                err.put("$ref", "../model/Error.yaml#/definitions/ErrorInfo");
                pro.put("error", err);
                continue;
            } else if ("requestId".equalsIgnoreCase(fieldNode.getName())) {
                LinkedHashMap<String, Object> requestId = new LinkedHashMap<>();
                requestId.put("type", "string");
                requestId.put("description", "请求ID");
                pro.put("requestId", requestId);
                continue;
            }
            // Response
            Map<String, Object> resultMap = getResultMap(filePath, operatorId, fieldNode);
            if (MapUtils.isNotEmpty(resultMap)) {
                pro.put("result", resultMap);
            }
        }
        schema.put("properties", pro);
        return schema;
    }

    private static Map<String, Object> getResultMap(String filePath, String operatorId, FieldNode fieldNode) {

        Map<String, Object> result = new LinkedHashMap<>();
        Map<String, ModelProBean> modelProMap = new LinkedHashMap<>();
        Map<String, Object> properties = getRecursion2(fieldNode, operatorId, modelProMap);
        result.put("properties", properties);
        if (MapUtils.isEmpty(modelProMap)) {
            return result;
        }
        for (Map.Entry<String, ModelProBean> modelEntry : modelProMap.entrySet()) {
            String filename = modelEntry.getKey();
            ModelProBean model = modelEntry.getValue();
            // 生成 Model Yml 文件
            YmlUtils.ymlModel(filePath, filename,
                    YmlUtils.generateModelMap(model.getModelData(), filename, model.getDescription(), false));
        }
        return result;
    }

    private static Map<String, Object> getRecursion2(FieldNode fieldNode, String operatorId, Map<String, ModelProBean> modelProMap) {
        if (Objects.isNull(fieldNode)) {
            return new LinkedHashMap<>();
        }
        if (Objects.isNull(fieldNode.getChildNode())) {
            return YmlUtils.generateObjectMap(fieldNode);
        }
        List<FieldNode> childNodeList = fieldNode.getChildNode().getChildNodes();
        if (Objects.isNull(childNodeList)) {
            return YmlUtils.generateObjectMap(fieldNode);
        }

        Map<String, Object> properties = new LinkedHashMap<>();
        for (FieldNode node : childNodeList) {
            if (Objects.isNull(node)) {
                continue;
            }
            String nodeName = node.getName();
            if (Objects.isNull(node.getChildNode())) {
                properties.put(nodeName, YmlUtils.generateObjectMap(node));
                continue;
            }
            Boolean isList = node.getChildNode().isList();

            String filename = operatorId + node.getType().replaceAll(YmlConstant.ARRAY_CLEAN, "");
            ModelProBean modelProBean = ModelProBean.builder().isList(isList)
                    .description(node.getDescription())
                    .filename(filename).build();
            List<FieldNode> fieldNodes = node.getChildNode().getChildNodes();
            // 1. Bean内部递归引用，生成Model
            if (CollectionUtils.isEmpty(fieldNodes) && node.getLoopNode()) {
                properties.put(nodeName, genModelSchemaMap(filename, filename, node.getDescription(), isList));
                modelProBean.setModelData(properties);
                modelProMap.put(filename, modelProBean);
                continue;
            } else if (CollectionUtils.isEmpty(fieldNodes)) {
                properties.put(nodeName, YmlUtils.generateObjectMap(node));
                continue;
            }
            // 2. 生成Model
            Map<String, Object> modelMap = getRecursion2(node, operatorId, modelProMap);
            modelProBean.setModelData(modelMap);
            modelProMap.put(filename, modelProBean);
            // 3. 生成VO引用链接
            properties.put(nodeName, genModelSchemaMap(filename, filename, node.getDescription(), isList));
        }
        return properties;
    }

    private static Map<String, Object> genModelSchemaMap(String filename, String claName, String desc, boolean isList) {
        LinkedHashMap<String, Object> schema = new LinkedHashMap<>();
        if (isList) {
            LinkedHashMap<String, Object> items = new LinkedHashMap<>();
            items.put("$ref", MessageFormat.format(YmlConstant.BASE_PATH, filename, claName));
            schema.put("items", items);
        } else {
            schema.put("$ref", MessageFormat.format(YmlConstant.BASE_PATH, filename, claName));
        }
        schema.put("type", isList ? "array" : "object");
        schema.put("description", desc);
        return schema;
    }
}
